#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <asm/types.h>
//该头文件需要放在netlink.h前面防止编译出现__kernel_sa_family未定义
#include <sys/socket.h>  
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
//#include <net/if.h>
#include <linux/if.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFLEN 20480  

#if 0
int get_gateway(char *gateway)  
{ 
	struct nlmsghdr *nlMsg;  
	struct rtmsg *rtMsg;  
	char buf[BUFLEN];  
	int retval;
	int sock, len, msgSeq = 0;  

	if((sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)  
	{  
		perror("Socket Creation: ");  
		return -1;  
	}  

	memset(buf, 0, BUFLEN);  

	nlMsg = (struct nlmsghdr *)buf;  
	rtMsg = (struct rtmsg *)NLMSG_DATA(nlMsg);  

	nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); // Length of message.  
	nlMsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table .  

	nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump.  
	nlMsg->nlmsg_seq = msgSeq++; // Sequence of the message packet.  
	nlMsg->nlmsg_pid = getpid(); // PID of process sending the request.  

	if(send(sock, nlMsg, nlMsg->nlmsg_len, 0) < 0){  
		printf("Write To Socket Failed:%s\n",strerror(errno));  
		return -1;  
	}  

	struct nlmsghdr *nh;  
	struct rtattr *rtAttr;  
	int rtLen;  
	struct in_addr dst,gate;  

	while((retval=recv(sock,buf,BUFLEN,0)>0))
	{
		for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval))  
		{  
			if (nh->nlmsg_type == NLMSG_DONE)  
				break;  
			else if(((nh->nlmsg_flags & NLM_F_MULTI) == 0)||(nh->nlmsg_type == NLMSG_ERROR))
			{
				fprintf(stderr,"msg error!\n");
				close(sock);
				return -1;
			}
			else if((nh->nlmsg_seq != msgSeq) || (nh->nlmsg_pid != getpid()))
			{
				//continue;
			}
		
			rtMsg = (struct rtmsg *)NLMSG_DATA(nh);  
			// If the route is not for AF_INET or does not belong to main routing table  
			//then return.   
			if((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))  
			{
				close(sock);
				return -1;
			}

			char ifname[16];
			unsigned int gateWay,srcAddr,dstAddr;
			rtAttr = (struct rtattr *)RTM_RTA(rtMsg);  
			rtLen = RTM_PAYLOAD(nh);  
			for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){  
				switch(rtAttr->rta_type) {  
					case RTA_OIF:  
						if_indextoname(*(int *)RTA_DATA(rtAttr), ifname);  
						break;  
					case RTA_GATEWAY:  
						gateWay = *(u_int *)RTA_DATA(rtAttr);  
						break;  
					case RTA_PREFSRC:  
						srcAddr = *(u_int *)RTA_DATA(rtAttr);  
						break;  
					case RTA_DST:  
						dstAddr = *(u_int *)RTA_DATA(rtAttr);  
						break;  
				}
			}  
			dst.s_addr = dstAddr;  
			if (strstr((char *)inet_ntoa(dst), "0.0.0.0"))  
			{  
				printf("oif:%s",ifname);  
				gate.s_addr = gateWay;  
				printf("gw: %s\n",(char *)inet_ntoa(gate));  
				gate.s_addr = srcAddr;  
				printf("src:%s\n",(char *)inet_ntoa(gate));  
				gate.s_addr = dstAddr;  
				printf("dst:%s\n",(char *)inet_ntoa(gate));   
			}
			close(sock);
			return 0;
		}
	}

	close(sock);  
	return 0;  
}
#endif

#if 1
int monitor_network_change() 
{  
    int fd, retval;  
    char buf[BUFLEN] = {0};  
    int len = BUFLEN;  
    struct sockaddr_nl addr;  
    struct nlmsghdr *nh;  
    struct ifinfomsg *ifinfo;  
    struct rtattr *attr;  
  
    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);  
    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len));  
    memset(&addr, 0, sizeof(addr));  
    addr.nl_family = AF_NETLINK;  
    addr.nl_groups = RTNLGRP_LINK;  
    bind(fd, (struct sockaddr*)&addr, sizeof(addr));  

    while ((retval = read(fd, buf, BUFLEN)) > 0)  
    {  
        for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval))  
        {  
            if (nh->nlmsg_type == NLMSG_DONE)  
                break;  
            else if (nh->nlmsg_type == NLMSG_ERROR)  
			{
				close(fd);
                return -1;  
			}
            else if (nh->nlmsg_type != RTM_NEWLINK)  
                continue;  
            ifinfo = NLMSG_DATA(nh);  
            printf("%u: %s", ifinfo->ifi_index,  
                    (ifinfo->ifi_flags & IFF_LOWER_UP) ? "up" : "down" );  
            attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifinfo)));  
            len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));  
            for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))  
            {  
                if (attr->rta_type == IFLA_IFNAME)  
                {  
                    printf(" %s", (char*)RTA_DATA(attr));  
                    break;  
                }  
            }  
            printf("\n");  
        }  
    }  
  
	close(fd);
    return 0;  
} 
#endif

int MonitorNetlinkUevent()
{
    int sockfd;
    struct sockaddr_nl sa;
    int len;
    char buf[4096];
    struct iovec iov;
    struct msghdr msg;
    int i;

    memset(&sa,0,sizeof(sa));
    sa.nl_family=AF_NETLINK;
    sa.nl_groups=NETLINK_KOBJECT_UEVENT;
    sa.nl_pid = 0;//getpid(); both is ok
    memset(&msg,0,sizeof(msg));
    iov.iov_base=(void *)buf;
    iov.iov_len=sizeof(buf);
    msg.msg_name=(void *)&sa;
    msg.msg_namelen=sizeof(sa);
    msg.msg_iov=&iov;
    msg.msg_iovlen=1;

    sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);
    if(sockfd==-1)
	{
        printf("socket creating failed:%s\n",strerror(errno));
		return -1;
	}
    if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa))==-1)
	{
        printf("bind error:%s\n",strerror(errno));
		return -1;
	}

    len=recvmsg(sockfd,&msg,0);
    if(len<0)
        printf("receive error\n");
    else if(len<32||len>sizeof(buf))
        printf("invalid message");
    for(i=0;i<len;i++)
        if(*(buf+i)=='\0')
            buf[i]='\n';
    printf("received %d bytes\n%s\n",len,buf);
	return 0;
}

int main(int argc,char **argv)
{
    //MonitorNetlinkUevent(); // Device Event etc.
	monitor_network_change();
	//char gw[32];
	//get_gateway(gw);

    return 0;
}

